#include "t_httpreply.h"
#include <typeinfo>

#include <QtScript>

T_HttpReply_1::T_HttpReply_1(
		QNetworkAccessManager::Operation op,
		QNetworkRequest request,
		QIODevice * outgoingData,
		QObject * parent)
			: T_HttpReply(op, request, outgoingData, parent)
{
	qDebug() << QString("ctor[%1::%1()]").arg(this->metaObject()->className()) << m_url;
	qDebug() << QString("ctor[%1::%1()]").arg(typeid(this).name()) << m_url;
	m_netReply = QNetworkAccessManager::createRequest(m_op, m_request, m_outgoingData);
    // connect
    p_connectQNetworkReply(m_netReply);
}
// abort()
/* virtual */ void T_HttpReply_1::abort()
{
	m_netReply->abort();
}

// raw headers:
/* virtual */ bool T_HttpReply_1::hasRawHeader(const QByteArray &headerName) const
{
	return m_netReply->hasRawHeader(headerName);
}
/* virtual */ QList<QByteArray> T_HttpReply_1::rawHeaderList() const
{
	return m_netReply->rawHeaderList();
}
/* virtual */ QByteArray T_HttpReply_1::rawHeader(const QByteArray &headerName) const
{
	return m_netReply->rawHeader(headerName);
}

// attributes
/* virtual */ QVariant T_HttpReply_1::attribute(QNetworkRequest::Attribute code) const
{
	return m_netReply->attribute(code);
}

// reading
/* virtual */ qint64 T_HttpReply_1::bytesAvailable()
{
	return m_netReply->bytesAvailable();
}
/* virtual */ QByteArray T_HttpReply_1::readInternal(qint64 a_max)
{
	return m_netReply->read(a_max);
}

T_HttpReply_2::T_HttpReply_2(
		QNetworkAccessManager::Operation op,
		QNetworkRequest request,
		QIODevice * outgoingData,
		QObject * parent)
			: T_HttpReply_1(op, request, outgoingData, parent)
{
    ////m_response = new QByteArray();
    QBuffer * v_buffer = new QBuffer();
    v_buffer->setParent(this);
    v_buffer->open(QIODevice::WriteOnly);
    m_response = v_buffer;
    m_video_id = m_url.queryItemValue("video_id");
}

T_HttpReply_2::~T_HttpReply_2()
{
    if(m_response) delete m_response;
}

// emit handlers
/* virtual */ void T_HttpReply_2::emit_finished()
{
	QString v_content_type = m_netReply->header(QNetworkRequest::ContentTypeHeader).toString();
	qDebug() << "before p_parseStreamMap()" << v_content_type;

	if(v_content_type == "application/x-www-form-urlencoded")
	{
        p_parseStreamMap(m_video_id, ((QBuffer *)m_response)->buffer());
	}
	else if(v_content_type.startsWith("text/html"))
	{
        QString v_html = QString::fromUtf8(((QBuffer *)m_response)->buffer());
        QRegExp v_rx_1("<param\\s+name=\\\\?\"flashvars\\\\?\"\\s+value=\\\\?\"([^\"\\\\]+)\\\\?\">");

        if(v_rx_1.indexIn(v_html) != -1)
		{
            qDebug() << "[MATCH](0)" << v_rx_1.cap(0);
            qDebug() << "[MATCH](1)" << v_rx_1.cap(1);
            QString v_form_urlencoded = v_rx_1.cap(1);
			QUrl v_from_query_items;
			v_from_query_items.setUrl("?"+v_form_urlencoded);
			QString v_video_id = v_from_query_items.queryItemValue("video_id");
			qDebug() << "[video_id]" << v_video_id;
			p_parseStreamMap(v_video_id, v_form_urlencoded.toUtf8());
		}
        else if(v_html.startsWith("while(1);"))
        {
            QString v_json = v_html.replace(QRegExp("^while\\(1\\);"),"");
            //qDebug() << "[v_json]" << v_json;
            QScriptEngine v_engine;
            QFile v_json2_js(":/jslib/json2.js");
            v_json2_js.open(QIODevice::ReadOnly);
            v_engine.evaluate(QString::fromUtf8(v_json2_js.readAll()));
            QScriptValue v_value = v_engine.evaluate(v_json);
            v_engine.globalObject().setProperty("my_var", v_value);
            qDebug() << "[v_value.toString()]" << v_value.toString();
            //qDebug() << "JSON.stringify(my_var)" << v_engine.evaluate("JSON.stringify(my_var)").toString();
            qDebug() << "JSON.stringify(my_var,null,2)" << v_engine.evaluate("JSON.stringify(my_var,null,2)").toString();
            qDebug() <<  "[v_value.isArray()]" << v_value.isArray();
            QVariant v_variant = v_value.toVariant();
            if(v_variant.type() == QVariant::List)
            {
                QVariantList v_var_list = v_variant.toList();
                qDebug() << "[v_var_list.size()]" <<  v_var_list.size();
            }
            QString v_video_id = v_value.property(0)
                    .property("data")
                    .property("swf_args")
                    .property("video_id")
                    .toString();
            QString v_fmt_stream_map = v_value.property(0)
                    .property("data")
                    .property("swf_args")
                    .property("fmt_stream_map")
                    .toString();
            QString v_params = QString("video_id=%1&fmt_stream_map=%2").arg(v_video_id).arg(v_fmt_stream_map);
            p_parseStreamMap(v_video_id, v_params.toUtf8());
        }
        else
        {
			qDebug() << "[NO-MATCH]";
			qDebug() << v_html;
		}
	}
	emit finished();
}

// private functions
void T_HttpReply_2::p_parseStreamMap(QString a_id, QByteArray a_bytes)
{
	qDebug() << "T_HttpReply_2::p_parseStreamMap()" << a_id;
	//[Get Token]
	QByteArray v_params_raw = a_bytes;
	v_params_raw.prepend("?");
	QUrl v_params_url;
	v_params_url.setEncodedUrl(v_params_raw);
	QString v_token = v_params_url.queryItemValue("token");
	QString v_status = v_params_url.queryItemValue("status");
	QString v_reason = v_params_url.queryItemValue("reason");
	QString v_allow_embed = v_params_url.queryItemValue("allow_embed");
	QString v_fmt_stream_map = v_params_url.queryItemValue("fmt_stream_map");
	////qDebug() << v_status << v_allow_embed << v_token << v_fmt_stream_map;
	qDebug() << "[v_token]" << v_token;
	QMap<int, QUrl> a_fmt_map;
	a_fmt_map.clear();
	if(v_fmt_stream_map != "")
	{
		QStringList v_slist_1 = v_fmt_stream_map.split(',');
		for(int i=0; i<v_slist_1.size(); i++)
		{
			QStringList v_slist_2 = v_slist_1.at(i).split('|');
			int v_fmt = v_slist_2.at(0).toInt();
			QString v_stream_url_1 = v_slist_2.at(1);
			QUrl v_stream_url_2;
			v_stream_url_2.setEncodedUrl(v_stream_url_1.toLatin1());
			v_stream_url_2 = s_unify_url(v_stream_url_2);
			a_fmt_map.insert(v_fmt, v_stream_url_2);
			s_url_info[v_stream_url_2] = QString("%1:%2").arg(a_id).arg(v_fmt);
			s_redirect_info[v_stream_url_2] = QString("%1:%2").arg(a_id).arg(v_fmt);
		}
	}
	qDebug() << "[a_fmt_map.keys()]" << a_fmt_map.keys();
	qDebug() << "[s_url_info()]" << s_url_info;
}

T_HttpReply_3::T_HttpReply_3(
        QNetworkAccessManager::Operation op,
        QNetworkRequest request,
        QIODevice * outgoingData,
        QObject * parent)
            : T_HttpReply(op, request, outgoingData, parent)
            , m_headReply(0)
            ////, m_netReply(0)
            ////, m_metaDataChanged(false)
            , m_readCounter(0)
{
    qDebug() << "[T_HttpReply_3::T_HttpReply_3()]" << m_url;

    // HEAD op
    m_headReply = QNetworkAccessManager::head(m_request);
    m_headReply->setParent(this);
    NetworkBatchRequest v_head_nbr(m_headReply);
    v_head_nbr.wait();
    QString v_content_type = m_headReply->header(QNetworkRequest::ContentTypeHeader).toString();
    qint64 v_content_len = m_headReply->header(QNetworkRequest::ContentLengthHeader).toLongLong();
    qDebug() << "[T_HttpReply_3::T_HttpReply_3()]" << v_content_type << v_content_len;

    m_cache_file_name = p_url_cache_file_name();
    m_cache_file.setFileName(m_cache_file_name);
    m_cache_offset = 0;

    if(m_cache_file.open(QIODevice::ReadWrite))
    {
        qDebug() << "[REIRECT]:no";
        m_cache_offset = m_cache_file.size();
    }

    qDebug() << "[T_HttpReply_3::T_HttpReply_3()]" << m_cache_file_name << m_cache_file.isOpen() << m_cache_file.size();

    if(m_cache_offset == 0)
    {
        //
    }
    else if(m_cache_offset < v_content_len)
    {
        QString v_range = QString("bytes=%1-").arg(m_cache_offset);
        qDebug() << v_range;
        m_request.setRawHeader("Range", v_range.toLatin1());
    }
    else // everything is cached already
    {
        QMetaObject::invokeMethod(this, "emit_metaDataChanged", Qt::QueuedConnection);
        QMetaObject::invokeMethod(this, "emit_readyRead", Qt::QueuedConnection);
        return;
    }

    m_netReply = QNetworkAccessManager::createRequest(m_op, m_request, m_outgoingData);
    p_connectQNetworkReply(m_netReply);
}

// abort()
/* virtual */ void T_HttpReply_3::abort()
{
    ////m_netReply->abort();
}

// raw headers:
/* virtual */ bool T_HttpReply_3::hasRawHeader(const QByteArray &headerName) const
{
    return m_headReply->hasRawHeader(headerName);
}
/* virtual */ QList<QByteArray> T_HttpReply_3::rawHeaderList() const
{
    return m_headReply->rawHeaderList();
}
/* virtual */ QByteArray T_HttpReply_3::rawHeader(const QByteArray &headerName) const
{
    return m_headReply->rawHeader(headerName);
}

// attributes
/* virtual */ QVariant T_HttpReply_3::attribute(QNetworkRequest::Attribute code) const
{
    return m_headReply->attribute(code);
}

// reading
/* virtual */ qint64 T_HttpReply_3::bytesAvailable()
{
    if(!m_readyRead) return 0;
    qint64 v_total = m_netReply ? m_netReply->bytesAvailable() : 0;
    if(m_readCounter < m_cache_offset)
    {
        v_total += (m_cache_offset - m_readCounter);
    }
    return v_total;
}
/* virtual */ QByteArray T_HttpReply_3::readInternal(qint64 a_max)
{
    if(!m_readyRead)  return QByteArray();
    QByteArray v_result;
    if(m_readCounter < m_cache_offset)
    {
        qint64 v_read_len = (m_cache_offset - m_readCounter);
        if(v_read_len > a_max) v_read_len = a_max;
        m_cache_file.seek(m_readCounter);
        QByteArray v_bytes = m_cache_file.read(v_read_len);
        v_result.append(v_bytes);
        m_readCounter += v_bytes.size();
    }
    if(v_result.size() >= a_max)
    {
        if(m_cache_offset > 0) qDebug() << "T_HttpReply_3::read()" << v_result.size();
        return v_result;
    }
    a_max -= v_result.size();
    if(!m_netReply)
    {
        QMetaObject::invokeMethod(this, "emit_finished", Qt::QueuedConnection);
    }
    else if(m_netReply->bytesAvailable())
    {
        qint64 v_read_len = m_netReply->bytesAvailable();
        if(v_read_len > a_max) v_read_len = a_max;
        QByteArray v_bytes = m_netReply->read(v_read_len);
        v_result.append(v_bytes);
        if(m_cache_file.isOpen())
        {
            qDebug() << "[T_HttpReply_3::read()] WRITING!";
            m_cache_file.seek(m_readCounter);
            m_cache_file.write(v_bytes);
            m_cache_file.flush();
        }
        m_readCounter += v_bytes.size();
    }
    if(m_cache_offset > 0) qDebug() << "T_HttpReply_3::read()" << v_result.size();
    return v_result;
}

//private functions
QString T_HttpReply_3::p_url_cache_file_name()
{
    QString v_name;
    QUrl v_unify_url = s_unify_url(m_url);
    if(s_url_info.contains(v_unify_url))
    {
        QString v_second = s_url_info.value(v_unify_url);
        v_name = v_second;
        v_name.replace(":", "_");
        v_name += ".flv";
    }
    else
    {
        v_name = m_url.toEncoded();
        QRegExp v_rx("(&fexp=[^&]*)|(&signature=[^&]*)");
        v_name.replace(v_rx, "");
        qDebug() << "[v_name]" << v_name;
        QCryptographicHash v_md5(QCryptographicHash::Md5);
        v_md5.reset();
        v_md5.addData(v_name.toLatin1());
        QByteArray v_hash = v_md5.result();
        v_name = v_hash.toHex();
    }
    QString v_dirPath =
            QDesktopServices::storageLocation(
                    QDesktopServices::DesktopLocation);
    QDir dir(v_dirPath);
    QString v_filePath = dir.filePath(v_name);
    return v_filePath;
}
